package edu.zju.gis.dldsj.type;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yanlo yanlong_lee@qq.com
 * @version 1.0 2018/07/12
 * Comments 描述结构化数据表的字段信息
 * <p>
 * updated by hls
 * 2018/07/13 增加了基于反射的构造函数
 */
public class StructType implements Iterable<StructField> {

    private List<StructField> fields;

    public StructType(@NotNull List<StructField> fields) {
        this.fields = fields;
    }

    public StructType() {
        this(new ArrayList<>());
    }

    /**
     * @param clazz 数据库对应model类
     *              仅支持DataType Enum中支持的类别
     */
    public StructType(Class<?> clazz) {
        this.fields = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            String name = field.getName();
            String type = field.getGenericType().toString();
            DataType dataType = DataType.valueOf(type.substring(type.lastIndexOf(".") + 1).toUpperCase());
            this.fields.add(new StructField(name, dataType));
        }
    }

    public void addField(StructField field) {
        this.fields.add(field);
    }

    public void addField(String name, DataType dataType) {
        this.fields.add(new StructField(name, dataType));
    }

    public void addField(String name, DataType dataType, boolean nullable) {
        this.fields.add(new StructField(name, dataType, nullable));
    }

    public void addField(String name, DataType dataType, boolean nullable, Object defaultValue) {
        this.fields.add(new StructField(name, dataType, nullable, defaultValue));
    }

    public int getFieldCount() {
        return fields.size();
    }

    public List<StructField> getFields() {
        return fields;
    }

    public StructField getField(int i) {
        return fields.get(i);
    }

    public StructField getField(String name) throws NoSuchFieldException {
        for (StructField field : fields) {
            if (field.getName().equals(name))
                return field;
        }
        throw new NoSuchFieldException("there is not field named '" + name + "'");
    }

    public boolean contains(String name) {
        for (StructField field : fields)
            if (field.getName().equals(name))
                return true;
        return false;
    }

    public List<String> fieldNames() {
        return fields.stream().map(StructField::getName).collect(Collectors.toList());
    }

    public Map<String, StructField> nameToField() {
        return fields.stream().collect(Collectors.toMap(StructField::getName, $ -> $));
    }

    public Map<String, Integer> nameToIndex() {
        return fields.stream().collect(Collectors.toMap(StructField::getName, $ -> fields.indexOf($)));
    }

    public Map<Integer, StructField> indexToField() {
        return fields.stream().collect(Collectors.toMap($ -> fields.indexOf($), $ -> $));
    }

    public int getFieldIndex(String name) {
        for (int i = 0; i < fields.size(); i++) {
            if (fields.get(i).getName().equals(name))
                return i;
        }
        return -1;
    }

    @Override
    @NotNull
    public Iterator<StructField> iterator() {
        if (fields == null)
            return Collections.emptyIterator();
        else
            return fields.iterator();
    }
}
